package repositories

import (
	"context"
	"github.com/pkg/errors"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/response"
	"gorm.io/gorm"
	"strconv"
	"strings"
)

// ApplicationRepository define interface of application repository
type ApplicationRepository interface {
	FindByID(ctx context.Context, ID uint64) (*models.Application, error)
	FindAll(ctx context.Context) (applications []*models.Application, err error)
	Save(ctx context.Context, app *models.Application) error
	Find(ctx context.Context, name string, level int, groupIDs []string) (applications []*models.Application, err error)
	DeleteById(ctx context.Context, ID uint64) error
	FindByName(ctx context.Context, name string) (*models.Application, error)
	FindCountByLevel(ctx context.Context, group []string) ([]*response.StatItem, error)
	FindByNameAndGroupForPage(ctx context.Context, name string, group string, level int, pageSize, pageNum int) (applications []*models.Application, totalCount int64, err error)
}

type applicationRepository struct {
	db *gorm.DB
}

// NewApplicationRepository define constructor of application repository
func NewApplicationRepository(db *gorm.DB) ApplicationRepository {
	return &applicationRepository{db: db}
}

// FindByID find an application by id
func (r *applicationRepository) FindByID(ctx context.Context, ID uint64) (*models.Application, error) {
	var app = new(models.Application)
	if err := r.db.WithContext(ctx).Table(models.ApplicationTableName).Where("id = ?", ID).First(&app).Error; err != nil {
		return nil, errors.Wrap(err, "find application by id error")
	}

	return app, nil
}

func (r *applicationRepository) Find(ctx context.Context, name string, level int, groupIDs []string) (applications []*models.Application, err error) {
	query := ""
	if level > 0 {
		query += " and level=" + strconv.Itoa(level)
	}
	groupIDs2 := make([]string, len(groupIDs))
	copy(groupIDs2, groupIDs)
	if len(groupIDs2) > 0 {
		groupID := "("
		for i := range groupIDs2 {
			groupIDs2[i] = "\"" + groupIDs2[i] + "\""
		}
		groupID += strings.Join(groupIDs2, ",") + ")"
		query += " and dev_group_id in " + groupID
	}
	if err := r.db.WithContext(ctx).Table(models.ApplicationTableName).Find(&applications, "name like ? "+query, "%"+name+"%").Error; err != nil {
		return nil, errors.Wrap(err, "find all error")
	}
	return applications, nil
}

func (r *applicationRepository) FindAll(ctx context.Context) (applications []*models.Application, err error) {
	if err := r.db.WithContext(ctx).Table(models.ApplicationTableName).Find(&applications).Error; err != nil {
		return nil, errors.Wrap(err, "find all error")
	}
	return applications, nil
}

func (r *applicationRepository) Save(ctx context.Context, app *models.Application) error {
	if err := r.db.WithContext(ctx).Table(models.ApplicationTableName).Save(app).Error; err != nil {
		return errors.Wrap(err, "save app error")
	}
	return nil
}

func (r *applicationRepository) DeleteById(ctx context.Context, ID uint64) error {
	if err := r.db.WithContext(ctx).Table(models.ApplicationTableName).Delete(models.Application{}, "id = ?", ID).Error; err != nil {
		return errors.Wrap(err, "delete app by id error")
	}
	return nil
}

func (r *applicationRepository) FindByName(ctx context.Context, name string) (*models.Application, error) {
	var app = new(models.Application)
	if err := r.db.WithContext(ctx).Table(models.ApplicationTableName).Where("name = ?", name).First(&app).Error; err != nil {
		return nil, errors.Wrap(err, "find app by name error")
	}
	return app, nil
}
func (r *applicationRepository) FindCountByLevel(ctx context.Context, groups []string) ([]*response.StatItem, error) {
	var apps = make([]*response.StatItem, 0)
	if err := r.db.WithContext(ctx).Raw("select level,count(*) app_count from t_app where dev_group_id in ?   group by level", groups).Scan(&apps).Error; err != nil {
		return nil, errors.Wrap(err, "FindCountByLevel app by group error")
	}
	return apps, nil
}

func (r *applicationRepository) FindByNameAndGroupForPage(ctx context.Context, name, group string, level int, pageSize, pageNum int) (applications []*models.Application, totalCount int64, err error) {
	db := r.db.WithContext(ctx).Table(models.ApplicationTableName)
	if name != "" {
		db = db.Where("name like ?", "%"+name+"%")
	}
	if group != "" {
		db = db.Where("dev_group_id= ?", group)
	}
	if level > 0 {
		db = db.Where("level= ?", level)
	}

	if err := db.Count(&totalCount).Error; err != nil {
		return nil, 0, errors.Wrap(err, "find application by name error")
	}
	if err := db.Order("update_time desc").Limit(pageSize).Offset(pageSize * (pageNum - 1)).
		Find(&applications).Error; err != nil {
		return nil, 0, errors.Wrap(err, "find application by name error")
	}

	return applications, totalCount, nil
}
